using System;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

using BloomPostprocess;
using Microsoft.Kinect;
using System.IO;
using E_Motion;
using System.Threading;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Media;

namespace Emotion
{
    public class Wall : Microsoft.Xna.Framework.Game
    {
		private SpriteFont font;
        private Texture2D burninTexture, activePlayersTexture, shadowTexture, tempTexture;
		private GraphicsDeviceManager graphics;
		private SpriteBatch spriteBatch;
		private double[] pixelTime;
        private ShawnBloomComponent shawnBloom;
		private KinectSensor sensor;
        private bool changeColorOverTime;


        //Sound Output
        private SoundEffect playerEnterSFX;
        private SoundEffect playerLeaveSFX;
        //private SoundEffectInstance movementSound;
        private SoundEffectInstance[] movementSounds = new SoundEffectInstance[PLAYERS_MAX];
        Song movementSong;
        private int sumChange;
        
        private RenderTarget2D soundWall;

        private static readonly Microsoft.Xna.Framework.Color[] PLAYER_COLORS = {
			//Microsoft.Xna.Framework.Color.White, // White :P
			new Microsoft.Xna.Framework.Color(65, 255, 65), // Neon green
            new Microsoft.Xna.Framework.Color(255, 65, 255), // Pink
            new Microsoft.Xna.Framework.Color(65, 65, 255), // Blue
            new Microsoft.Xna.Framework.Color(255, 255, 65), // Yellow
            new Microsoft.Xna.Framework.Color(255, 65, 65), // Red
            new Microsoft.Xna.Framework.Color(65, 255, 255), // Light blue
        };

        private const int PLAYERS_MAX = 6;

        //If a new player enters, what color (index) should he get?
        private int nextColor = 0;
        private bool[] playersInPreviousFrame = new bool[PLAYERS_MAX];
        private int[] playerToColorMap = new int[PLAYERS_MAX];

        private static readonly Microsoft.Xna.Framework.Color[] PLAYER_BURNIN_COLORS = {
			//Microsoft.Xna.Framework.Color.White, // White :P
			new Microsoft.Xna.Framework.Color(10, 255, 10), // Neon green
            new Microsoft.Xna.Framework.Color(255, 10, 255), // Pink
            new Microsoft.Xna.Framework.Color(10, 10, 255), // Blue
            new Microsoft.Xna.Framework.Color(255, 255, 10), // Yellow
            new Microsoft.Xna.Framework.Color(255, 10, 10), // Red
            new Microsoft.Xna.Framework.Color(10, 255, 255), // Light blue
        };

		// Intermediate storage for the depth data received from the camera
		//private short[] depthPixels;
        private DepthImagePixel[] depthData;
        private short minDepth = short.MaxValue, maxDepth = short.MinValue;
        private bool useDepthForAlpha;
       
		// Intermediate storage for the depth data converted to color
        private float[] colorPixels;
        private byte[] activePlayersData, shadow, prevActivePlayersData;

        private bool shakeOn, audioAffectBloom, audioBurnIn, noiseOn, colorMode;

        const float BLOOM_INTENSITY_START = 8;
        const float BLOOM_THRESHOLD_START = 0.01f;

        const int AUDIO_SHAKE_POWER = 50;
        const float AUDIO_SCALING_POWER = 0.25f;
        private double audioShake = 0;

        #region Audio stuff

        /// <summary>
        /// Number of milliseconds between each read of audio data from the stream.
        /// </summary>
        private const int AudioPollingInterval = 50;

        /// <summary>
        /// Number of samples captured from Kinect audio stream each millisecond.
        /// </summary>
        private const int SamplesPerMillisecond = 16;

        /// <summary>
        /// Number of bytes in each Kinect audio stream sample.
        /// </summary>
        private const int BytesPerSample = 2;

        /// <summary>
        /// Number of audio samples represented by each column of pixels in wave bitmap.
        /// </summary>
        private const int SamplesPerColumn = 40;

        /// <summary>
        /// Width of bitmap that stores audio stream energy data ready for visualization.
        /// </summary>
        private const int EnergyBitmapWidth = 780;

        /// <summary>
        /// Height of bitmap that stores audio stream energy data ready for visualization.
        /// </summary>
        private const int EnergyBitmapHeight = 195;

        /// <summary>
        /// Array of background-color pixels corresponding to an area equal to the size of whole energy bitmap.
        /// </summary>
        private readonly byte[] backgroundPixels = new byte[EnergyBitmapWidth * EnergyBitmapHeight];

        /// <summary>
        /// Buffer used to hold audio data read from audio stream.
        /// </summary>
        private readonly byte[] audioBuffer = new byte[AudioPollingInterval * SamplesPerMillisecond * BytesPerSample];

        /// <summary>
        /// Buffer used to store audio stream energy data as we read audio.
        /// 
        /// We store 25% more energy values than we strictly need for visualization to allow for a smoother
        /// stream animation effect, since rendering happens on a different schedule with respect to audio
        /// capture.
        /// </summary>
        private readonly double[] energy = new double[(uint)(EnergyBitmapWidth * 1.25)];

        /// <summary>
        /// Stream of audio being captured by Kinect sensor.
        /// </summary>
        private Stream audioStream;

        /// <summary>
        /// <code>true</code> if audio is currently being read from Kinect stream, <code>false</code> otherwise.
        /// </summary>
        private bool reading;

        /// <summary>
        /// Thread that is reading audio from Kinect stream.
        /// </summary>
        private Thread readingThread;

        /// <summary>
        /// Sum of squares of audio samples being accumulated to compute the next energy value.
        /// </summary>
        private double accumulatedSquareSum;

        /// <summary>
        /// Number of audio samples accumulated so far to compute the next energy value.
        /// </summary>
        private int accumulatedSampleCount;

        /// <summary>
        /// Index of next element available in audio energy buffer.
        /// </summary>
        private int energyIndex;

        /// <summary>
        /// Number of newly calculated audio stream energy values that have not yet been
        /// displayed.
        /// </summary>
        private int newEnergyAvailable;

        #endregion

        public Wall()
        {
            graphics = new GraphicsDeviceManager(this);

            graphics.PreferredBackBufferWidth = 1024;
            graphics.PreferredBackBufferHeight = 768;

            Content.RootDirectory = "Content";

            shawnBloom = new ShawnBloomComponent(this);
            shawnBloom.BloomIntensity = BLOOM_INTENSITY_START;
            shawnBloom.BlurPasses = 3;
            shawnBloom.BloomThreshold = BLOOM_THRESHOLD_START;
            Components.Add(shawnBloom);

            // Set options
            changeColorOverTime = false;
            audioAffectBloom = false;
            shakeOn = false;
            useDepthForAlpha = false;
            audioBurnIn = false;
            colorMode = false;
        }
 
        protected override void Initialize()
        {
            base.Initialize();

            sensor = KinectSensor.KinectSensors[0];

            // Turn on the depth stream to receive depth frames
            sensor.DepthStream.Enable(DepthImageFormat.Resolution320x240Fps30);

            // To get player index
            sensor.SkeletonStream.Enable();

            // Allocate space to put the depth pixels we'll receive
           // depthPixels = new short[sensor.DepthStream.FramePixelDataLength];
            depthData = new DepthImagePixel[sensor.DepthStream.FramePixelDataLength];
            // Allocate space for time values
            pixelTime = new double[sensor.DepthStream.FramePixelDataLength];
            

            // Allocate space to put the color pixels we'll create
            colorPixels     = new float[sensor.DepthStream.FramePixelDataLength * sizeof(int)];
            activePlayersData   = new byte[sensor.DepthStream.FramePixelDataLength * sizeof(int)];
            prevActivePlayersData = new byte[sensor.DepthStream.FramePixelDataLength * sizeof(int)];
            shadow          = new byte[sensor.DepthStream.FramePixelDataLength * sizeof(int)];

            soundWall = new RenderTarget2D(graphics.GraphicsDevice, 320, 240, false, SurfaceFormat.Color, DepthFormat.None, 10, RenderTargetUsage.PreserveContents);

            // Add an event handler to be called whenever there is new depth frame data
            sensor.DepthFrameReady += SensorDepthFrameReady;

            // Start the sensor!
            sensor.Start();

            // Start streaming audio!
            this.audioStream = this.sensor.AudioSource.Start();

            // Use a separate thread for capturing audio because audio stream read operations
            // will block, and we don't want to block main UI thread.
            this.reading = true;
            this.readingThread = new Thread(AudioReadingThread);
            this.readingThread.Start();
        }

        #region Audio reading
        /// <summary>
        /// Handles polling audio stream and updating visualization every tick.
        /// </summary>
        private void AudioReadingThread()
        {
            // Bottom portion of computed energy signal that will be discarded as noise.
            // Only portion of signal above noise floor will be displayed.
            const double EnergyNoiseFloor = 0.2;

            while (this.reading)
            {
                int readCount = audioStream.Read(audioBuffer, 0, audioBuffer.Length);

                for (int i = 0; i < readCount; i += 2)
                {
                    // compute the sum of squares of audio samples that will get accumulated
                    // into a single energy value.
                    short audioSample = BitConverter.ToInt16(audioBuffer, i);
                    this.accumulatedSquareSum += audioSample * audioSample;
                    ++this.accumulatedSampleCount;

                    if (this.accumulatedSampleCount < SamplesPerColumn)
                    {
                        continue;
                    }

                    // Each energy value will represent the logarithm of the mean of the
                    // sum of squares of a group of audio samples.
                    double meanSquare = this.accumulatedSquareSum / SamplesPerColumn;
                    double amplitude = Math.Log(meanSquare) / Math.Log(int.MaxValue);

                    // Renormalize signal above noise floor to [0,1] range.
                    this.energy[this.energyIndex] = Math.Max(0, amplitude - EnergyNoiseFloor) / (1 - EnergyNoiseFloor);
                    this.energyIndex = (this.energyIndex + 1) % this.energy.Length;

                    this.accumulatedSquareSum = 0;
                    this.accumulatedSampleCount = 0;
                    ++this.newEnergyAvailable;

                    audioShake = Math.Max(0, Math.Max(audioShake - 0.001, (amplitude - 0.85) * 10));
                }
            }
        }
        #endregion

        protected override void OnExiting(object sender, EventArgs args)
        {
            // Tell audio reading thread to stop and wait for it to finish.
            this.reading = false;

            if (null != readingThread)
            {
                readingThread.Join();
            }

            if (null != this.sensor)
            {
                this.sensor.AudioSource.Stop();

                this.sensor.Stop();
                this.sensor = null;
            }

            base.OnExiting(sender, args);
        }

        /// <summary>
        /// Event handler for Kinect sensor's DepthFrameReady event
        /// </summary>
        /// <param name="sender">object sending the event</param>
        /// <param name="e">event arguments</param>
        private void SensorDepthFrameReady(object sender, DepthImageFrameReadyEventArgs e)
        {
            using (DepthImageFrame depthFrame = e.OpenDepthImageFrame())
            {
                
                if (depthFrame != null)
                {
                    // Copy the pixel data from the image to a temporary array
                    
                    depthFrame.CopyDepthImagePixelDataTo(depthData);

                    //Find players in this frame:
                    bool[] playersThisFrame = new bool[PLAYERS_MAX];
                    for (int i = 0; i < this.depthData.Length; ++i)
                    {
                        short playerIndex = depthData[i].PlayerIndex;
                        if (playerIndex > 0)
                        {
                            playersThisFrame[playerIndex-1] = true;
                        }
                        

                    }
                    for (int i = 0; i < playersThisFrame.Length; ++i)
                    {
                        //New player detected
                        if (playersThisFrame[i] && !playersInPreviousFrame[i])
                        {
                            playerEnterSFX.Play();
                            //Get your color
                            playerToColorMap[i] = nextColor;
                            
                            nextColor++;
                            if (nextColor >= PLAYER_COLORS.Length)
                            {
                                nextColor = 0;
                            }
                        }
                        else if (!playersThisFrame[i] && playersInPreviousFrame[i])
                        {
                            // player left
                            playerLeaveSFX.Play();
                        }
                    }
                    playersInPreviousFrame = playersThisFrame;
                    

                    // Adjust movement sound volume
                    for (int playerIndex = 0; playerIndex < playersThisFrame.Length; playerIndex++)
                    {
                        if (playersThisFrame[playerIndex])
                        {
                            if (noiseOn && movementSounds[playerIndex].State != SoundState.Playing)
                            {
                                movementSounds[playerIndex].Volume = 0;
                                movementSounds[playerIndex].Play();
                            }

                            movementSounds[playerIndex].Volume = MathHelper.Lerp(movementSounds[playerIndex].Volume, 1, 0.025f);
                        }
                        else
                        {
                            movementSounds[playerIndex].Volume = MathHelper.Lerp(movementSounds[playerIndex].Volume, 0, 0.025f);

                            if (movementSounds[playerIndex].Volume < 0.1f)
                            {
                                movementSounds[playerIndex].Pause();
                            }
                        }
                    }
                    


                    // Convert the depth to RGB
                    int colorPixelIndex = 0;
                    for (int pixelIndex = 0; pixelIndex < this.depthData.Length; ++pixelIndex)
                    {
                        //byte playerIndex = (byte)(this.depthPixels[pixelIndex] & DepthImageFrame.PlayerIndexBitmask);

                        short playerIndex = depthData[pixelIndex].PlayerIndex;
                        if (playerIndex > 0 && playerIndex <= MAX_PLAYERS)
                        {
                            Microsoft.Xna.Framework.Color color;
                            if (colorMode)
                            {
                                color = Color.White;
                            }
                            else
                            {
                                color = PLAYER_COLORS[playerToColorMap[playerIndex-1]];
                            }
                            Microsoft.Xna.Framework.Color colorBurnIn = PLAYER_BURNIN_COLORS[playerToColorMap[playerIndex - 1]];


                            Microsoft.Xna.Framework.Color currentColor = new Microsoft.Xna.Framework.Color((byte)colorPixels[colorPixelIndex], (byte)colorPixels[colorPixelIndex + 1], (byte)colorPixels[colorPixelIndex + 2]);
                            Microsoft.Xna.Framework.Color drawColor;
                            
                            if (colorMode && !color.Equals(currentColor))
                            {
                                /*float lerpValue = 0.005f;
                                if (colorPixels[colorPixelIndex + 3] >= 255)
                                {
                                    lerpValue = 1;
                                }*/
                                float lerpValue = 1 - (float)colorPixels[colorPixelIndex + 3] / 255f * 0.95f;
                                colorPixels[colorPixelIndex] = MathHelper.Lerp(colorPixels[colorPixelIndex], colorBurnIn.R, lerpValue);
                                colorPixels[colorPixelIndex + 1] = MathHelper.Lerp(colorPixels[colorPixelIndex + 1], colorBurnIn.G, lerpValue);
                                colorPixels[colorPixelIndex + 2] = MathHelper.Lerp(colorPixels[colorPixelIndex + 2], colorBurnIn.B, lerpValue);
                                drawColor = new Microsoft.Xna.Framework.Color((byte)colorPixels[colorPixelIndex], (byte)colorPixels[colorPixelIndex + 1], (byte)colorPixels[colorPixelIndex + 2]);
                                //drawColor = Microsoft.Xna.Framework.Color.Lerp(currentColor, colorBurnIn, 0.05f/*1 - (float)colorPixels[colorPixelIndex + 3] / 255f * 0.95f*/);
                            }
                            else
                            {
                                drawColor = colorBurnIn;
                            }
                           
                            // Write out red byte
                            colorPixels[colorPixelIndex] = drawColor.R; // colorBurnIn.R;// (byte)(color.R * 0.70f);
                            shadow[colorPixelIndex] = 0;
                            activePlayersData[colorPixelIndex++] = color.R;

                            // Write out green byte
                            colorPixels[colorPixelIndex] = drawColor.G; // colorBurnIn.G;// (byte)(color.G * 0.70f);
                            shadow[colorPixelIndex] = 0;
                            activePlayersData[colorPixelIndex++] = color.G;

                            // Write out blue byte                        
                            colorPixels[colorPixelIndex] = drawColor.B; // colorBurnIn.B; //(byte)(color.B * 0.70f);
                            shadow[colorPixelIndex] = 0;
                            activePlayersData[colorPixelIndex++] = color.B;
                           
                            short depth = depthData[pixelIndex].Depth;
                            if (depth < minDepth)
                            {
                                minDepth = depth;
                            }
                            if (depth > maxDepth)
                            {
                                maxDepth = depth;
                            }

                            // Write out alpha byte
                            byte intensity;
                            if (useDepthForAlpha)
                            {
                                float ratio = (1 - (float)(depth - minDepth) / (maxDepth - minDepth));
                                intensity = (byte)(255 * ratio);
                                pixelTime[pixelIndex]+= ratio;
                            }
                            else
                            {
                                intensity = 255;
                                pixelTime[pixelIndex]++;
                            }
                            
                            colorPixels[colorPixelIndex] = intensity;

                            // Write out alpha byte
                            activePlayersData[colorPixelIndex] = intensity;
                            shadow[colorPixelIndex] = 255;
                        }
                        else
                        {
                            colorPixelIndex += 3;

                            activePlayersData[colorPixelIndex] = 0;
                            shadow[colorPixelIndex] = 0;
                            
                            if (pixelTime[pixelIndex] <= 0)
                            {
                                colorPixels[colorPixelIndex] = 0;
                            }
                            else
                            {
                                pixelTime[pixelIndex] -= 0.25 + 0.001 * pixelTime[pixelIndex];

                                if(audioBurnIn)
                                    colorPixels[colorPixelIndex] = (byte)MathHelper.Lerp((float)colorPixels[colorPixelIndex], (float)Math.Max(0, pixelTime[pixelIndex] * 255), 0.1f);
                                else
                                    colorPixels[colorPixelIndex] = (byte)MathHelper.Lerp((float)colorPixels[colorPixelIndex], (float)Math.Max(0, -Math.Exp(LOG_DIV_TIME * pixelTime[pixelIndex]) + 1) * 255, 0.1f);
                            }
                            
                            if(changeColorOverTime) {
                                DecreaseColorOverTime(pixelIndex, colorPixelIndex);
                            }
                        }

                        colorPixelIndex++;
                    }

                    // Create textures
                    if (activePlayersTexture == null)
                    {
                        tempTexture = new Texture2D(GraphicsDevice, sensor.DepthStream.FrameWidth, sensor.DepthStream.FrameHeight);
                    }
                    byte[] colorPixelsAsBytes = new byte[colorPixels.Length];
                    for (int i = 0; i < colorPixelsAsBytes.Length; i++)
                    {
                        colorPixelsAsBytes[i] = (byte)colorPixels[i];
                    }
                    tempTexture.SetData<byte>(colorPixelsAsBytes);

                    if (activePlayersTexture == null)
                    {
                        activePlayersTexture = new Texture2D(GraphicsDevice, sensor.DepthStream.FrameWidth, sensor.DepthStream.FrameHeight);
                    }
                    activePlayersTexture.SetData<byte>(activePlayersData);

                    if (shadowTexture == null)
                    {
                        
                        shadowTexture = new Texture2D(GraphicsDevice, sensor.DepthStream.FrameWidth, sensor.DepthStream.FrameHeight);
                    }
                    
                    shadowTexture.SetData<byte>(shadow);

                    
                    burninTexture = tempTexture;



                    //See overall change in player data ("overall velocity")

                    sumChange = 0;

                    for (int i = 3; i < activePlayersData.Length; i+= 4)
                    {
                        if (activePlayersData[i] != prevActivePlayersData[i])
                        {
                            sumChange++;
                        }

                    }
                    float volume = sumChange / 7000f;
                    if (volume > 1)
                    {
                        volume = 1;
                    }
                    //movementSound.Volume = volume;
                    MediaPlayer.Volume = volume;
                    //Console.WriteLine(volume);
                    activePlayersData.CopyTo(prevActivePlayersData, 0);


                }
            }
        }

        private static double LOG_0_1 = Math.Log(0.1);
        private const int TIME_TO_REACH_HIGH_VALUE = 100;
        private static double LOG_DIV_TIME = LOG_0_1 / TIME_TO_REACH_HIGH_VALUE;
        private short MAX_PLAYERS = 6;

        private static double CalculateColor(double timeOn, byte valueNow)
        {
            double color = -Math.Exp(LOG_DIV_TIME * timeOn) + 1;
            double currentColor = valueNow / 255f;
            return (double)Math.Max(0, MathHelper.Lerp((float)currentColor, (float)color, 1.0f));
        }

        private void DecreaseColorOverTime(int pixelIndex, int alphaIndex)
        {
            for (int i = 3; i > 0; i--)
                if (colorPixels[alphaIndex - i] > 200)
                    colorPixels[alphaIndex - i] = (byte)(255 * CalculateColor(pixelTime[pixelIndex], (byte)this.colorPixels[alphaIndex - i]));
        }

        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);
            playerEnterSFX = this.Content.Load<SoundEffect>("Enter");
            playerLeaveSFX = this.Content.Load<SoundEffect>("Leave");
            
            font = this.Content.Load<SpriteFont>("font");

            //movementSound = playerLeaveSFX.CreateInstance();
            //movementSound.IsLooped = true;
            //movementSound.Play();

            SoundEffect movementSound = Content.Load<SoundEffect>("Movement");
            for (int playerIndex = 0; playerIndex < movementSounds.Length; playerIndex++)
            {
                movementSounds[playerIndex] = movementSound.CreateInstance();
                movementSounds[playerIndex].IsLooped = true;
            }

            MediaPlayer.IsRepeating = true;
        }

        const int ELEVATION_SPEED = 3;
        const int ELEVATION_COOLDOWN = 90;
        int elevationCooldown = 0;

        KeyboardState state;
        KeyboardState lastState;
        

        private bool KeyPressed(Keys key)
        {
            return (state.IsKeyDown(key) && lastState.IsKeyUp(key));
        }

        protected override void Update(GameTime gameTime)
        {
			base.Update(gameTime);

            state = Keyboard.GetState();

			// Terminate the program
            if (state.IsKeyDown(Keys.Escape))
            {
                sensor.Stop();
                this.Exit();
            }

            // Toggle shake
            if (KeyPressed(Keys.S))
            {
                shakeOn = !shakeOn;
            }

            // Toggle bloom audio effect
            if (KeyPressed(Keys.B))
            {
                audioAffectBloom = !audioAffectBloom;
                shawnBloom.BlurPasses = 3;
            }

            // Toggle useDepthForAlpha
            if (KeyPressed(Keys.D))
            {
                useDepthForAlpha = !useDepthForAlpha;
            }

            // Toggle audioBurnIn
            if (KeyPressed(Keys.A))
            {
                audioBurnIn = !audioBurnIn;
            }

            // Toggle noise
            if (KeyPressed(Keys.N))
            {
                noiseOn = !noiseOn;
            }

            // Toggle noise
            if (KeyPressed(Keys.C))
            {
                colorMode = !colorMode;

                if (colorMode)
                {
                    shawnBloom.BloomThreshold = 0.5f;
                }
                else
                {
                    shawnBloom.BloomThreshold = BLOOM_THRESHOLD_START;
                }
            }

            if (KeyPressed(Keys.D1))
            {
                movementSong = this.Content.Load<Song>("sleep");
                MediaPlayer.Play(movementSong);
                MediaPlayer.Volume = 1;
            }

            if (KeyPressed(Keys.D2))
            {
                movementSong = this.Content.Load<Song>("psy");
                MediaPlayer.Play(movementSong);
                MediaPlayer.Volume = 1;
            }

            if (KeyPressed(Keys.D0))
            {
                MediaPlayer.Stop();
            }

            if (KeyPressed(Keys.O))
            {
                shawnBloom.Visible = !shawnBloom.Visible;
                shawnBloom.Enabled = shawnBloom.Visible;
            }

			// Clear the burn-in
            if (state.IsKeyDown(Keys.Space))
            {
                for (int i = 0; i < pixelTime.Length; i++)
                {
                    pixelTime[i] = 0;
                }
            }

			// Move Kinect down
            if (state.IsKeyDown(Keys.Down))
            {
                if (elevationCooldown <= 0)
                {
                    sensor.ElevationAngle = Math.Max(sensor.MinElevationAngle, sensor.ElevationAngle - ELEVATION_SPEED);
                    elevationCooldown = ELEVATION_COOLDOWN;
                }
            }
			// Move Kinect up
            else if (state.IsKeyDown(Keys.Up))
            {
                if (elevationCooldown <= 0)
                {
                    sensor.ElevationAngle = Math.Min(sensor.MaxElevationAngle, sensor.ElevationAngle + ELEVATION_SPEED);
                    elevationCooldown = ELEVATION_COOLDOWN;
                }
            }

            elevationCooldown -= 1;

			// Toggle fullscreen
            if (state.IsKeyDown(Keys.F) || state.IsKeyDown(Keys.F4) || state.IsKeyDown(Keys.F11))
            {
				graphics.IsFullScreen = !graphics.IsFullScreen;
				graphics.ApplyChanges();
            }

            if (audioAffectBloom)
            {
                shawnBloom.BlurPasses = (int)(2 + audioShake * 8);
                //shawnBloom.BloomIntensity = (float)(BLOOM_INTENSITY_START + audioShake * 10);
            }

            lastState = state;
        }

        

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Microsoft.Xna.Framework.Color.Red);

			if (burninTexture != null)
			{
                GraphicsDevice device = graphics.GraphicsDevice;
                Viewport viewport = device.Viewport;

                // Test for capturing by sound
                if (audioBurnIn)//audioShake > 0.2) 
                {
                    device.SetRenderTarget(soundWall);

                    spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied);

                    spriteBatch.Draw(activePlayersTexture, new Microsoft.Xna.Framework.Rectangle(0, 0, 320, 240), Microsoft.Xna.Framework.Color.White);
                 
                    spriteBatch.End();
                    device.SetRenderTarget(null);
                }
                
                shawnBloom.BeginDraw();
                device.Clear(Microsoft.Xna.Framework.Color.Black);

                // Shake it!/?
                if (shakeOn)
                {
                    Random random = new Random();
                    Vector2 offset = new Vector2((float)(-audioShake + audioShake * random.NextDouble() * 2), (float)(-audioShake + audioShake * random.NextDouble() * 2)) * AUDIO_SHAKE_POWER;
                    float scaling = 1 +(float)(random.NextDouble() * audioShake) * AUDIO_SCALING_POWER;
                    spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, null, null, null, null, Matrix.CreateTranslation(-graphics.PreferredBackBufferWidth / 2, -graphics.PreferredBackBufferHeight / 2, 0) * Matrix.CreateScale(scaling) * Matrix.CreateTranslation(graphics.PreferredBackBufferWidth / 2, graphics.PreferredBackBufferHeight / 2, 0) * Matrix.CreateTranslation(offset.X, offset.Y, 0));
                }
                else
                {
                    spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied);
                }

                spriteBatch.Draw(soundWall, new Microsoft.Xna.Framework.Rectangle(0, 0, graphics.PreferredBackBufferWidth, graphics.PreferredBackBufferHeight), null, Microsoft.Xna.Framework.Color.White, 0, Vector2.Zero, SpriteEffects.None, 0);

                spriteBatch.Draw(activePlayersTexture, new Microsoft.Xna.Framework.Rectangle(0, 0, graphics.PreferredBackBufferWidth, graphics.PreferredBackBufferHeight), null, Microsoft.Xna.Framework.Color.White, 0, Vector2.Zero, SpriteEffects.None, 0);

                spriteBatch.Draw(burninTexture, new Microsoft.Xna.Framework.Rectangle(0, 0, graphics.PreferredBackBufferWidth, graphics.PreferredBackBufferHeight), null, Microsoft.Xna.Framework.Color.White, 0, Vector2.Zero, SpriteEffects.None, 0);
                
                spriteBatch.End();

                // Draw other components (which includes the bloom).
                base.Draw(gameTime);
        	}
			else
			{
                spriteBatch.Begin();
			    spriteBatch.DrawString(font, "Starting the camera, don't panic!", Vector2.Zero, Microsoft.Xna.Framework.Color.Red);
                spriteBatch.End();
            }

            base.Draw(gameTime);
        }

        
    }
}